/*  This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License, or (at
    your option) any later version.
    For more details, see the GNU Lesser General Public License (www.fsf.org
    or the COPYING file somewhere in the package)
 */

#include "Bundle.hh"

/*! @file src/OSGi/Bundle.cc
    @brief Class OSGi::Bundle (non-inline) methods.
    @author @ref Guillaume_Terrissol
    @date 13th December 2009 - 16th October 2014
    @note This file is distributed under the LGPL license.
    Refer to the file COPYING (or http://www.fsf.org) for more information.
 */

#if defined(_WIN32)
#   include <windows.h>
    // Because this size is defined in stand-alone libzip.
#   if defined(MAX_PATH)
#       undef MAX_PATH
#   endif
#   define MAX_PATH 1024
#else
#   if !defined(ZIP_STD)
#       define ZIP_STD
#   endif   // ZIP_STD
#   include <dlfcn.h>
#endif  // defined(_WIN32)

#include <sys/stat.h>

#include <fstream>
#include <set>
#include <sstream>

#include "Activator.hh"
#include "CGuard.hh"
#include "ConfigFile.hh"
#include "Context.hh"
#include "Exception.hh"
#include "Platform.hh"
#include "Properties.hh"
#include "unzip.h"
#include "Utils.hh"
#include "Version.hh"

namespace OSGi
{
    /// @cond DEVELOPMENT

//------------------------------------------------------------------------------
//                               Helper Functions
//------------------------------------------------------------------------------

    namespace
    {
        /*! Reads useful pieces of information from a manifest.
            @param pManifest Manifest filename
            @param pProps    Bundle properties to update with manifest data
         */
        void getManifestInfos(const std::string& pManifest, Properties& pProps)
        {
            std::fstream    lStream{pManifest.c_str(), std::fstream::in | std::fstream::binary};

            if (lStream.is_open())
            {
                // List of regexp used to identify the manifest fields
                static std::map<std::string, std::string>  sFields =
                {
                    { std::string{"Name"},           std::string{"Bundle-Name:"} },
                    { std::string{"SymbolicName"},   std::string{"Bundle-SymbolicName:"} },
                    { std::string{"Version"},        std::string{"Bundle-Version:"} },
                    { std::string{"Vendor"},         std::string{"Bundle-Vendor:"} },
                    { std::string{"Copyright"},      std::string{"Bundle-Copyright:"} },
                    { std::string{"Activator"},      std::string{"Bundle-Activator:"} },
                    { std::string{"Library"},        std::string{"; library ="} }
                };

                // Manifest fields values.
                std::map<std::string, std::string>  lValues;
                std::string                         lLine;
                do
                {
                    std::getline(lStream, lLine, '\n');

                    for(const auto& lRE : sFields)
                    {
                        size_t  lPos    = std::string::npos;
                        if ((lValues.count(lRE.first) == 0) && ((lPos = lLine.find(lRE.second)) != std::string::npos))
                        {
                            std::string lValue = trimString(lLine.substr(lPos + lRE.second.length()));
                            lValues[lRE.first] = lValue;
                            break;
                        }
                    }
                }
                while(!lStream.eof());

                // Default library.
                if (lValues.count("Library") == 0)
                {
                    lValues["Library"] = lValues["SymbolicName"];
                }

                for(const auto& lValue : lValues)
                {
                    auto    lKey = std::string{"bundle."} + static_cast<char>(std::tolower(lValue.first[0])) + lValue.first.substr(1);
                    pProps.set(lKey, lValue.second);
                }
            }
        }


        /*! Retrieves a bundle dependencies from a manifest content
            @param pManifest     Manifest filename
            @param pDependencies Dependency list to fill up
            @param pContext      Execution context
         */
        void getDependencies(const std::string& pManifest, std::map<std::string, Version>& pDependencies, Bundle::ContextCPtr pContext)
        {
            std::fstream    lStream{pManifest.c_str(), std::fstream::in | std::fstream::binary};

            if (lStream.is_open())
            {
                // List of regexp used to identify the manifest fields
                std::string lDependencyField{"Require-Bundle:"};

                // Manifest fields values.
                std::string lLine;
                do
                {
                    std::getline(lStream, lLine, '\n');
                }
                while(!lStream.eof() && (lLine.find(lDependencyField) == std::string::npos));

                if (!lLine.empty())
                {
                    lLine = trimString(lLine.substr(lLine.find(lDependencyField) + lDependencyField.length()));
                    const std::string   lVersionField{"bundle-version"};

                    for(bool lHasNext = true; lHasNext; lHasNext = (*lLine.rbegin() == ','), std::getline(lStream, lLine, '\n'), trimString(lLine))
                    {
                        std::string lBundleName = trimString(lLine.substr(0, std::min(lLine.find(','), lLine.find(';'))));
                        lLine = lLine.substr(lBundleName.length() + 1);
                        size_t  lFieldPos = lLine.find(lVersionField);
                        if (lFieldPos != std::string::npos)
                        {
                            std::string lVersionText = lLine.substr(lLine.find('=', lFieldPos + lVersionField.length()) + 1);
                            if (*lVersionText.rbegin() == ',')
                            {
                                lVersionText.resize(lVersionText.length() - 1);
                            }
                            trimString(lVersionText);
                            try
                            {
                                pDependencies.insert(std::make_pair(lBundleName, Version{lVersionText}));
                            }
                            catch(Exception& e)
                            {
                                if (pContext->isVerbose()) pContext->err() << "Invalid version : " << e.what() << std::endl;
                            }
                        }
                        else
                        {
                            pDependencies[lBundleName]; // No version.
                        }
                    }
                }
            }
        }
    }


//------------------------------------------------------------------------------
//                                Bundle Private
//------------------------------------------------------------------------------

    /*! @brief Bundle private implementation.
        @version 0.7
        @internal
        @note ContextCPtr is redefined from Bundle because of an ICE on mingw
     */
    class Bundle::Private
    {
    public:
        //! @name Types
        //@{
        using ContextCPtr  = Bundle::ContextCPtr;                               //!< Constant pointer to context.
        using WeakPtr      = std::weak_ptr<Bundle>;                             //!< (Weak) pointer to bundle.
        using Dependencies = std::map<std::string, Version>;                    //!< Dependency list.
        using Dependees    = std::set<WeakPtr, std::owner_less<WeakPtr>>;       //!< Dependee list.
        //@}
                Private(std::string pFilename, int pId, ContextCPtr pContext);  //!< Constructor.
        //! @name Reading
        //@{
        void    readManifest(HZIP pZip, ContextCPtr pContext);                  //!< Reading manifest data.
        void    readProperties(HZIP pZip, ContextCPtr pContext);                //!< Reading properties.
        //@}
        //! @name Operations
        //@{
        bool    extractFiles(HZIP pZip);                                        //!< Binaries extraction.
        bool    loadActivator(ContextPtr pContext);                             //!< Activator loading.
        //@}
        void    setState(const std::string& pNew);                              //!< State change.
        bool    dependsOn(Bundle::CPtr pOther) const;                           //!< Dependency checking.
        //! @name Attributes
        //@{
        Dependencies    mDependencies;                                          //!< Bundle dependencies.
        Dependees       mDependees;                                             //!< Bundle dependees.
        LifeCycle::Ptr  mLifeCycle;                                             //!< Bundle state machine.
        Version         mVersion;                                               //!< Bundle version.
        Properties      mProps;                                                 //!< Bundle properties.
        CGuard<void>    mHandle;                                                //!< Bundle main library handle.
        std::string     mName;                                                  //!< Bundle symbolic name.
        int             mId;                                                    //!< Bundle Id.
        Activator*      mActivator;                                             //!< Bundle activator.
        time_t          mBundleTime;                                            //!< Bundle creation date.
        std::string     mState;                                                 //!< Bundle current state.
        std::string     mTmpDir;                                                //!< Temporary folder.
        std::string     mCacheDir;                                              //!< Cache folder for binaries.
        //@}
    };


//------------------------------------------------------------------------------
//                           Bundle Private : Constructor
//------------------------------------------------------------------------------

    /*! @copydetails Bundle::Bundle(std::string, int, ContextCPtr)
     */
    Bundle::Private::Private(std::string pFilename, int pId, ContextCPtr pContext)
        : mDependencies{}
        , mDependees{}
        , mLifeCycle{pContext->bundleFactory()->makeBundleLifeCycle()}
        , mVersion{}
        , mProps{}
        , mHandle{}
        , mName{"undefined yet"}
        , mId{pId}
        , mActivator{}
        , mBundleTime{}
        , mState{kUninstalled}
        , mTmpDir{pContext->temporaryPath()}
        , mCacheDir{pContext->cachePath()}
    {
        CGuard<std::remove_pointer<HZIP>::type> lZip(OpenZip(pFilename.c_str(), ""), CloseZip);

        if (lZip.get() == nullptr)
        {
            throw IOError{"Failed to open " + fromWorkingDirectory(pFilename)};
        }

        struct stat lStats;
        stat(pFilename.c_str(), &lStats);
        mBundleTime = lStats.st_mtime;

        readManifest(lZip.get(), pContext);
        readProperties(lZip.get(), pContext);

        if      (auto lSame = pContext->findBundle(pId))
        {
            if (lSame->name() != mName)
            {
                throw BundleError{"Bundle Id " + std::to_string(pId) + " already in use"};
            }
            // else same id, same name : the bundle is being replaced.
        }
        else if (pContext->findBundle(mName))
        {
            throw BundleError{"Bundle " + mName + " already defined with different Id"};
        }

        if (extractFiles(lZip.get()))
        {
            setState(mLifeCycle->initialState());
        }
    }


//------------------------------------------------------------------------------
//                           Bundle Private : Reading
//------------------------------------------------------------------------------

    /*! @param pZip     Zip file handle
        @param pContext Execution context
     */
    void Bundle::Private::readManifest(HZIP pZip, ContextCPtr pContext)
    {
        int lIndex = -1;
        if (FindZipItem(pZip, "META-INF/manifest.mf", false, &lIndex, nullptr) != ZR_OK)
        {
            throw BundleError{"Missing manifest"};
        }

        std::string lManifestFile   = mTmpDir + "/manifest.mf";
        if (UnzipItem(pZip, lIndex, lManifestFile.c_str()) == ZR_OK)
        {
            if (std::fstream{lManifestFile.c_str(), std::fstream::in}.is_open())
            {
                std::string lLibName;
                std::string lActivatorName;
                getManifestInfos(lManifestFile, mProps);
                getDependencies(lManifestFile, mDependencies, pContext);
                remove(lManifestFile.c_str());

                return;
            }
        }

        throw IOError{"Manifest hasn't been extracted"};
    }


    /*! @param pZip Zip file handle
        @param pContext Execution context
     */
    void Bundle::Private::readProperties(HZIP pZip, ContextCPtr pContext)
    {
        std::string lName           = mProps.get("bundle.symbolicName");
        std::string lPropertiesFile = lName + ".properties";

        int lIndex = -1;
        if (FindZipItem(pZip, lPropertiesFile.c_str(), false, &lIndex, nullptr) != ZR_OK)
        {
            if (pContext->properties()->getBool("checked"))
            {
                throw BundleError{"Missing bundle " + lName + " properties"};
            }
        }

        std::string lPropertiesPath = mTmpDir + "/" + lPropertiesFile;
        if (UnzipItem(pZip, lIndex, lPropertiesPath.c_str()) == ZR_OK)
        {
            ConfigFile lConfigFile{lPropertiesPath};
            lConfigFile.update(mProps, "");
            remove(lPropertiesPath.c_str());
        }
        else if (pContext->properties()->getBool("checked"))
        {
            throw IOError{"Properties haven't been extracted"};
        }
        mVersion = Version{mProps.get("bundle.version")};
        mName    = lName;
    }


//------------------------------------------------------------------------------
//                          Bundle Private : Operations
//------------------------------------------------------------------------------

    /*! @param pZip Zip file handle
        @retval true On success
        @retval false On failure
     */
    bool Bundle::Private::extractFiles(HZIP pZip)
    {
        std::string lBinFiles           = std::string{"bin/"} + Platform::osName() + "/";
        std::string lBinFilesSpecific   = std::string{"bin/"} + Platform::osName() + "/" + Platform::osArchitecture() + "/";
        ZIPENTRY    lEntry;

        if (GetZipItem(pZip, -1, &lEntry) == ZR_OK)
        {
            std::map<std::string, int>  lBinaries{};
            std::string                 lTmpDir = mTmpDir + "/" + mName + "/";
            int                         lCount  = lEntry.index;

            for(int lIndex = 0; lIndex < lCount; ++lIndex)
            {
                if (GetZipItem(pZip, lIndex, &lEntry) == ZR_OK)
                {
                    std::string lName       = lEntry.name;
                    // For binaries, looks for the specific version.
                    if (lName.compare(0, lBinFilesSpecific.length(), lBinFilesSpecific) == 0)
                    {
                        // If there was a [OS] generic, it will be replaced by a specific.
                        lBinaries[lName.substr(lBinFilesSpecific.length())] = lIndex;
                    }
                    else if (lName.compare(0, lBinFiles.length(), lBinFiles) == 0)
                    {
                        lName = lName.substr(lBinFiles.length());
                        if (lBinaries.count(lName) == 0)
                        {
                            // No specific so far, registers [OS] generic.
                            lBinaries[lName] = lIndex;
                        }
                    }
                    else
                    {
                        // The manifest and the properties are extracted again, and kept.
                        if (UnzipItem(pZip, lIndex, (lTmpDir + lName).c_str()) != ZR_OK)
                        {
                            return false;
                        }
                    }
                }
                else
                {
                    return false;
                }
            }
            // Now, deals with the binaries.
            for(auto lBinary : lBinaries)
            {
                std::string lFileInCache    = mCacheDir + "/" + lBinary.first;
                struct stat lStats;
                // If can't check the file in cache, or the file in the cache is older than the bundle, it may be overwritten.
                if (stat(lFileInCache.c_str(), &lStats) != 0 || lStats.st_ctime < mBundleTime)
                {
                    if (UnzipItem(pZip, lBinary.second, lFileInCache.c_str()) != ZR_OK)
                    {
                        return false;
                    }
                }

            }
            return true;
        }

        return false;
    }


    /*! @param pContext Execution context
        @retval true On success
        @retval false On failure
     */
    bool Bundle::Private::loadActivator(ContextPtr pContext)
    {
        std::string lLibrary    = mCacheDir + "/" + mProps.get("bundle.symbolicName");
#if defined(_WIN32)
        lLibrary += POSTFIX".dll";
        UINT    lPrevMode   = SetErrorMode(SEM_FAILCRITICALERRORS);
        mHandle = CGuard<void>{LoadLibrary(lLibrary.c_str()), [](void* pModule) -> void
        {
            FreeLibrary(static_cast<HMODULE>(pModule));
        }};

        if (mHandle.get() != nullptr)
        {
            HMODULE mdl = static_cast<HMODULE>(mHandle.get());
            FARPROC func = GetProcAddress(mdl, "getActivator");
            if (func != nullptr)
            {
                mActivator = reinterpret_cast<Activator* (*)(const char*)>(func)(mProps.get("bundle.activator").c_str());
            }
        }
        else
        {
            if (pContext->isVerbose()) pContext->err() << "Unabled to load " << fromWorkingDirectory(lLibrary) << " : error #" << GetLastError() << std::endl;
        }
        SetErrorMode(lPrevMode);
#else   // defined(_WIN32)
        lLibrary += POSTFIX".so";
        mHandle = CGuard<void>{dlopen(lLibrary.c_str(), RTLD_NOW | RTLD_GLOBAL), dlclose};

        if (mHandle.get() != nullptr)
        {
            void*   func = dlsym(mHandle.get(), "getActivator");
            if (func != nullptr)
            {
                mActivator = reinterpret_cast<Activator* (*)(const char*)>(func)(mProps.get("bundle.activator").c_str());
            }
        }
        else
        {
            if (pContext->isVerbose()) pContext->err() << "Unabled to load " << fromWorkingDirectory(lLibrary) << " because " << dlerror() << std::endl;
        }
#endif  // defined(_WIN32)

        return (mActivator != nullptr);
    }


//------------------------------------------------------------------------------
//                        Bundle Private : Other Methods
//------------------------------------------------------------------------------

    /*! @param pNew Bundle new @ref OSGi_Bundle_States_Group "state"
     */
    void Bundle::Private::setState(const std::string& pNew)
    {
        mState = pNew;
    }


    /*! Checks whether the bundle depends on another one.
        @param pOther Is this other bundle a dependency of the instance
        @retval true If @p pOther is a dependency of @b this
        @retval false Otherwise
     */
    bool Bundle::Private::dependsOn(Bundle::CPtr pOther) const
    {
        for(const auto& lDependee : pOther->pthis->mDependees)
        {
            if ((lDependee.lock()->name() == mName) || (dependsOn(lDependee.lock())))
            {
                return true;
            }
        }

        return false;
    }

    /// @endcond

//------------------------------------------------------------------------------
//                                 Bundle Sorter
//------------------------------------------------------------------------------

    /*! Allows sorting a bundle container, from the least dependent ones, the
        most dependent ones.
        @param pBundles Bundle container to sort (the bundles must be resolved
               already
        @note If all required bundle aren't in the container, the sort is
              partial
     */
    void Bundle::Sorter::operator()(std::vector<Bundle::Ptr>& pBundles) const
    {
        std::vector<Bundle::Ptr>    lSortedBundles;
        std::set<std::string>       lSortedBundleNames;

        while(!pBundles.empty())
        {
            size_t  lUnsortedBundleCount = pBundles.size();

            for(auto lBndl = begin(pBundles); lBndl != end(pBundles);)
            {
                bool    lIsReady = true;
                // If all the dependencies have been sorted, the bundle can be marked as sorted as well.
                for(const auto& lDepName : (*lBndl)->pthis->mDependencies)
                {
                    if (lSortedBundleNames.count(lDepName.first) == 0)
                    {
                        lIsReady = false;
                        break;
                    }
                }

                if (lIsReady)
                {
                    lSortedBundleNames.insert((*lBndl)->name());
                    lSortedBundles.push_back(*lBndl);
                    lBndl = pBundles.erase(lBndl);
                }
                else
                {

                    ++lBndl;
                }
            }

            if (lUnsortedBundleCount == pBundles.size())
            {
                // Sorted bundles are set first.
                pBundles.insert(begin(pBundles), begin(lSortedBundles), end(lSortedBundles));

                return;
            }
        }

        pBundles.swap(lSortedBundles);
    }


//------------------------------------------------------------------------------
//                       Bundle : Constructor & Destructor
//------------------------------------------------------------------------------

    /*! @param pFilename Bundle filename (with OSGi::kBndlExt extension)
        @param pId       Id du bundle
        @param pContext  Execution context
     */
    Bundle::Bundle(std::string pFilename, int pId, ContextCPtr pContext, const Key&)
        : pthis{pFilename, pId, pContext}
    { }


    /*! This destructor is virtual, even if the class has no other virtual
        method, in order to allow subclassing, and deleting through pointer to
        Bundle (because a BundleFactory can only return Bundle::Ptr).
     */
    Bundle::~Bundle() = default;


//------------------------------------------------------------------------------
//                                Bundle : Swaps
//------------------------------------------------------------------------------

    /*! @param pThat Bundle to swap data with
     */
    void Bundle::swap(Bundle& pThat)
    {
        pthis.swap(pThat.pthis);
    }


    /*! @param pThat Bundle to swap data with
     */
    void Bundle::swap(Bundle::Ptr pThat)
    {
        pthis.swap(pThat->pthis);
    }


//------------------------------------------------------------------------------
//                             Bundle : Information
//------------------------------------------------------------------------------

    /*! @return The bundle symbolic name
     */
    std::string Bundle::name() const
    {
        return pthis->mName;
    }


    /*! @return A unique (dynamically set) id 
     */
    int Bundle::id() const
    {
        return pthis->mId;
    }


    /*! @return The bundle properties
     */
    Properties* Bundle::properties()
    {
        return &pthis->mProps;
    }


    /*! @return The bundle properties
     */
    const Properties* Bundle::properties() const
    {
        return &pthis->mProps;
    }


    /*! @return The bundle current state
     */
    std::string Bundle::state() const
    {
        return pthis->mState;
    }


    /*! @return The bundle version
     */
    std::string Bundle::version() const
    {
        return pthis->mVersion.toString();
    }


    /*! @param pPath Resource filename (full path) in the bundle archive file
        @return A file stream opened on the required resource
     */
    Bundle::ResourcePtr Bundle::resource(const std::string& pPath) const
    {
        auto    lResourcePath   = pthis->mTmpDir + "/" + name() + "/" + pPath;
        auto    lResource       = std::make_shared<std::fstream>(lResourcePath.c_str(), std::fstream::in | std::fstream::binary);
        if (lResource->is_open())
        {
            return lResource;
        }

        return ResourcePtr();
    }


//------------------------------------------------------------------------------
//                               Bundle : Actions                               
//------------------------------------------------------------------------------

    /*! @param  pChange  Action to apply on the bundle
        @param  pContext Execution context
        @retval true If the action @p pChange succeeded
        @retval false Otherwise
     */
    bool Bundle::act(const std::string& pChange, ContextPtr pContext)
    {
        auto    lChange = pthis->mLifeCycle->change(pChange, state());
        bool    lResult = bool(lChange);

        if (!lResult)
        {
            return false;
        }

        if (lChange->doesRecurse() == LifeCycle::ERecurse::eThisLast)
        {
            for(const auto& lDependee : pthis->mDependees)
            {
                lResult = lDependee.lock()->act(pChange, pContext) && lResult;
            }
        }

        bool    lResultForThis = (*lChange)(this, pContext);

        if (lResultForThis)
        {
            // Just in case the new state hasn't been set yet.
            setState(lChange->toState());
        }
        lResult = lResult && lResultForThis;

        if (lChange->doesRecurse() == LifeCycle::ERecurse::eThisFirst)
        {
            for(const auto& lDependee : pthis->mDependees)
            {
                lResult = lDependee.lock()->act(pChange, pContext) && lResult;
            }
        }

        return lResult;
    }


//------------------------------------------------------------------------------
//                                Bundle : States
//------------------------------------------------------------------------------

    /*! @param pName New state name
     */
    void Bundle::setState(const std::string& pName)
    {
        pthis->setState(pName);
    }


    /*! This methods set the current state, temporarily, while calling act() :
        the "final" state will be set just before returning from act().
        @param pName New temporary state name
        @note A temporary state shouldn't be defined in the life cycle
     */
    void Bundle::setTemporaryState(const std::string& pName)
    {
        pthis->setState(pName);
    }


//------------------------------------------------------------------------------
//                               Bundle : Actions
//------------------------------------------------------------------------------

    /*! @param pContext Execution context
        @sa LifeCycle
        @signal Context::bundleStarting(), Context::bundleStarted()
     */
    bool Bundle::start(ContextPtr pContext)
    {
        try
        {
            if (state() == kResolved)
            {
                setTemporaryState(kStarting);
                OSGI_EMIT pContext->bundleStarting(shared_from_this());

                // The bundles that this one depends on shall be activated. If needed, they are activated now.
                for(const auto& lDependency : pthis->mDependencies)
                {
                    std::string lDepName    = lDependency.first;
                    auto        lDep        = pContext->findBundle(lDepName);

                    // Active already ?
                    if (lDep->state() != kActive)
                    {
                        // No : it is started (the dependent bundle is resolved already; otherwise, this instance wouldn't be).
                        if (!lDep->act(kStart, pContext))
                        {
                            setState(kResolved);
                            return false;
                        }
                    }
                }

                if (pthis->loadActivator(pContext))
                {
                    pthis->mActivator->start(pContext);

                    setState(kActive);
                    OSGI_EMIT pContext->bundleStarted(shared_from_this());

                    return true;
                }
                else
                {
                    // Start failed. Back to previous mode.
                    setState(kResolved);
                }
            }
        }
        catch(std::exception& e)
        {
            if (pContext->isVerbose()) pContext->err() << "Caught standard (" << typeid(e).name() << ") exception :" << e.what() << std::endl;
            setState(kResolved);
        }

        return false;
    }


    /*! @param pContext Execution context
        @sa LifeCycle
        @signal Context::bundleStopping(), Context::bundleStopped()
     */
    bool Bundle::stop(ContextPtr pContext)
    {
        try
        {
            if (state() == kActive)
            {
                setTemporaryState(kStopping);
                OSGI_EMIT pContext->bundleStopping(shared_from_this());

                // The bundles that depends on this one shall be stopped, or uninstalled, for it to be stopped.
                for(const auto& lDependee : pthis->mDependees)
                {
                    if ((lDependee.lock()->state() != kResolved) &&
                        (lDependee.lock()->state() != kUninstalled))
                    {
                        if (pContext->isVerbose()) pContext->err() << "Bundle " << lDependee.lock()->name() << " still active" << std::endl;
                        setState(kActive);
                        return false;
                    }
                }

                pthis->mActivator->stop(pContext);

                setState(kResolved);
                OSGI_EMIT pContext->bundleStopped(shared_from_this());

                return true;
            }
        }
        catch(std::exception& e)
        {
            setState(kActive);
        }

        return false;;
    }


    /*! @param pContext Execution context
        @sa LifeCycle
        @signal Context::bundleUninstalling(), Context::bundleUninstalled()
     */
    bool Bundle::uninstall(ContextPtr pContext)
    {
        if ((state() == kResolved) || (state() == kInstalled))
        {
            OSGI_EMIT pContext->bundleUninstalling(shared_from_this());
            if (state() == kResolved)
            {
                // Cleans activator and unloads dynamic library.
                pthis->mActivator = nullptr;
                pthis->mHandle.reset();
            }

            // Little check (must not remove too much).
            if (!name().empty() && (name().find("..") == std::string::npos))
            {
                // Deletes the temporary folder which holds the bundle non-binary files.
                removeDirectory(pthis->mTmpDir + "/" + name());
                setState(kUninstalled);
                OSGI_EMIT pContext->bundleUninstalled(shared_from_this());

                return true;
            }
        }

        return false;
    }


    /*! @param pContext Execution context
        @sa LifeCycle
        @signal Context::bundleResolving(), Context::bundleResolved()
     */
    bool Bundle::tryResolve(ContextPtr pContext)
    {
        if (state() == kInstalled)
        {
            OSGI_EMIT pContext->bundleResolving(shared_from_this());
            Private::WeakPtr    lThis = std::const_pointer_cast<Bundle>(pContext->findBundle(name()));

            for(const auto& lRequired : pthis->mDependencies)
            {
                Ptr lDependency = pContext->findBundle(lRequired.first);

                if (!lDependency || !(lRequired.second.contains(lDependency->pthis->mVersion)))
                {
                    if (pContext->isVerbose()) pContext->err() << "Missing dependency : " << lRequired.first << ' ' << lRequired.second.toString() << '\n';

                    return false;
                }
                if ((lDependency->state() == kInstalled) && !lDependency->tryResolve(pContext))
                {
                    if (pContext->isVerbose()) pContext->err() << "Required dependency : " << lRequired.first << ' ' << lRequired.second.toString() << " couldn't be resolved\n";

                    return false;
                }
                lDependency->pthis->mDependees.insert(lThis);
            }

            setState(kResolved);
            OSGI_EMIT pContext->bundleResolved(shared_from_this());

            return true;
        }

        return false;
    }


//------------------------------------------------------------------------------
//                                   Constant
//------------------------------------------------------------------------------

    /*! Every bundle file must have this extension.
     */
    const std::string   kBndlExt = ".bndl";
}
